Chapter 1:
Introduction

In our last lesson, we saw how to organize windows using layout managers. We also learned about panels, how they hold other components, and how to use them to organize regions of the larger window. And we talked about how to introduce scroll bars, which allow us to scroll an area of the window when there's too much to display in it.

In this lesson, we're going to take our GUI skills another step forward by learning how to add a menu bar to a window. Almost every GUI application has a menu bar across the top, right under the title bar. Our windows from the last two lessons didn't have menus. We're so used to seeing them that you probably thought something was missing from those windows, even if you couldn't quite put your finger on it.

Well, today we're going to fill in that empty piece by adding a menu bar with as many menus and menu items as we need. We're also going to see how to set up keyboard shortcuts for these menus and menu items so we can use ALT and CTRL key combinations to select them.

Finally, we're going to see how to improve on our use of listeners so that both menus and listeners are easier to set up and use.

Let's get this show on the road!

Chapter 2:
Menus, Menu Items, & Menu Bar

Our next GUI step will add menus to our windows. We use three Swing classes to build menus: JMenuBar, JMenu, and JMenuItem.

The JMenuBar class represents a menu bar that shows up just below the title bar of our window. A window can only have one menu bar. For those of you working on a Mac, the default Java menu bar position will look strange because it's in the window instead of at the top of the screen. It takes an extra line of code in the main() method to put the menu bar at the top of the screen. I'll show it to you when we get to the code.

The JMenu class creates individual menus for the menu bar, like the usual File, Edit, and Help menus that are in many applications. You can use this class to add as many menus as you need to the menu bar or to add submenus to other menus. JMenus can also create pop-up menus, which we usually associate with right-clicks, but we won't deal with them in this lesson.

Last in the menu hierarchy is the JMenuItem class, which creates the individual menu items in each menu. A menu can contain as many items as you need, but if the menu gets too long, it makes more sense to use submenus than to have one long menu. Examples of menu items include the usual Open and Save menu items in the File menu. Just so we have our terminology straight, here's what I'm talking about:




GUI terminology


Normally, we add a menu bar to our frame first, then we create menus to add to the menu bar, and finally, we create menu items to add to the menus. Let's give it a try.

Here is a bare-bones GUI window program that we will add menus to. We'll start with this example for a couple of lessons, and we'll later go on to add other features based on our menu options. But for now, here's the starting point for this lesson:

    import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GUIMenu { private JFrame frame;

public static void main (String[] args) { GUIMenu gui = new GUIMenu(); gui.start(); }

public void start() { frame = new JFrame("GUI Menus"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container contentPane = frame.getContentPane();

frame.setSize(300, 300); frame.setVisible(true); } }

If you run this program, you will see yet one more empty window. That's getting pretty boring, isn't it? So let's do something to improve it. Let's add menus!

Just to keep our program better organized, let's add a method that will build our menu for us. Since it will only be called internally, we'll make it a private method and name it makeMenus(). Our start() method will call the new method, so let's add the call first, then we'll write the method. Here is the call, which we'll put after the line that gets our content pane:

    makeMenus();
    

That was pretty simple, wasn't it? Now let's create our new method at the end of the class, just before the last closing bracket (}), like this:

    private void makeMenus()
{
}	
	

We already know we'll need a JMenuBar object, some JMenu objects, and some JMenuItem objects. So let's create some names in our new method that will let us refer to those objects, like this:

    JMenuBar menuBar;
JMenu menu;
JMenuItem menuItem;
	
    

Now that we have some names, let's put them to work. First, we need to create our menu bar object and attach it to our frame. That takes two statements that look like this:

    menuBar = new JMenuBar();
frame.setJMenuBar(menuBar);
	
    

The first line creates a new JMenuBar object. The second line uses the frame's setJMenuBar() method to attach the menu bar to the frame. As I mentioned earlier, a window can only have one menu bar, so we've just used up our limit.

If you run the program now, it won't look much different than it did before. If you look closely, you might see a small, almost invisible menu bar across the top of the frame. Since we haven't added any menus yet, there's nothing to show in it. So let's fix that little problem. These next two lines show you how to create a menu and add it to the menu bar:

menu = new JMenu("File");
menuBar.add(menu);
	
    

The first of these lines creates our first menu and gives it the label "File." The second line adds the menu to our menu bar. Menus will appear in the menu bar in the order we add them, from left to right. If you run the program now, it will look like this:




A window with a menu


Note for the Mac Java programmers out there: If you want the menu bar to jump to the top of the screen like a Mac menu should, add this line to your main() method:

    System.setProperty("apple.laf.useScreenMenuBar", "true");
    
    

That line will set Java's System property that controls where menu bars appear on a Mac. It won't work anywhere but on a Mac. Now, back to the main topic.

Our application is starting to look more like the windows we're used to seeing. You can see the menu bar at the top of the window and the File menu at its left edge. But if you click the menu, nothing happens yet because there is nothing in the menu to display. Let's add some menu items to it, like this:

    menuItem = new JMenuItem("New");
menu.add(menuItem);
menuItem = new JMenuItem("Open.");
menu.add(menuItem);
menuItem = new JMenuItem("Save");
menu.add(menuItem);
menuItem = new JMenuItem("Save As.");
menu.add(menuItem);
menu.addSeparator();
menuItem = new JMenuItem("Exit");
menu.add(menuItem);
	
    

You can see that creating these menu items is very similar to creating the menu. We just create a JMenuItem object and give its constructor the text we want to appear in the menu. Then we add the menu item to the menu.

Take a look at the third-to-last line in the code above: menu.addSeparator();. This command is unique to the JMenu class. It adds a separator line to the menu so we can group related items together. In this case, we added it between Save As . . . and Exit to separate the file processing menu items from the Exit item. If you run the program now and click File, you should see something like this:




A menu with items


TQA-19, 21 --- In case you're wondering, you can add another menu to an existing menu, too. If you add a menu to a menu, it creates a submenu that opens when you click its name in the first menu. You can layer menus as deep as needed, one inside another. Since you already have all the tools to do that, I'm going to leave it for you to try as an exercise in the assignment.

Now, as you will find out if you click any of the menu items in our File menu, none of them actually do anything yet. Believe it or not, you already know how to make them work. We make them work the same way we did buttons: with ActionListeners. We will add an ActionListener object to each menu item to "listen" for a mouse click.

We'll organize our menu actions a little differently than we did our button actions in the last lesson, though. Since an application can have any number of menus and menu items, putting all their actions into one ActionPerformed() method would make that method very long. It would also violate the good coding practice of only asking a method to do one thing. So we will perform each menu item's action in a separate method, and our ActionPerformed() method will call them when the time is right. Not only will that organize our program better, but it will make future changes to the program much easier to manage.

(And we will be changing it!)

Chapter 3:
ActionListeners for Menus

Let's go ahead and add ActionListeners to our menu items now. Once again, we'll have to make our program implement the ActionListener interface on the first line of the class, like this:

    public class GUIMenu implements ActionListener

After that's done, we can add ActionListeners to the individual action items, like this:

    menuItem.addActionListener(this);

We'll need a line like this for each menu item. That means we'll need to repeat this line five times. We'll add it after the line that creates a menu item and before the line that adds the item to the menu. Here's the first one as an example. I'll show you all of them in just a minute.

    menuItem = new JMenuItem("New");
menuItem.addActionListener(this);
menu.add(menuItem); 
	
    

The last thing we'll do before I show you an updated version of the program is add the ActionPerformed method that the ActionListener interface requires. We'll make it an empty method for now, but we'll add to it in just a minute. Let's put it at the end of our class, which now looks like this:

    import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GUIMenu implements ActionListener
{
private JFrame frame;

public static void main (String[] args)
{
GUIMenu gui = new GUIMenu();
gui.start();
}

public void start()
{
frame = new JFrame("GUI Menus");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();

makeMenus();

frame.setSize(300, 300);
frame.setVisible(true);
}

private void makeMenus()
{
JMenuBar menuBar;
JMenu menu;
JMenuItem menuItem;

menuBar = new JMenuBar();
frame.setJMenuBar(menuBar);

// set up the File menu
menu = new JMenu("File");
menuBar.add(menu);

// add File menu items
menuItem = new JMenuItem("New");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Open...");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Save");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Save As...");
menuItem.addActionListener(this);
menu.add(menuItem);
menu.addSeparator();
menuItem = new JMenuItem("Exit");
menuItem.addActionListener(this);
menu.add(menuItem);
}

public void actionPerformed(ActionEvent e)
{
}
}

Making Menus Work

At this point, clicking our menu items still doesn't do anything, but we'll fix that right now. Just like when we used multiple buttons, there are multiple menu items that can kick off an action event. We will need our actionPerformed() method to figure out which item got clicked so we can do the right thing. (My mom always told me it's important to do the right thing. Didn't yours?) With buttons, we used the event's getSource() method to figure out which button got clicked. Because our menus don't have individual names for references, we'll do it a little differently.

Every ActionEvent object has another method named getActionCommand() that will get the text string associated with the action. For menu items, that text is what's displayed in the menu when you click it. For example, the text for the first menu item is the string "New." Since we can get that text, we can check it to see which menu item's text it matches and proceed appropriately. For example, we can check for the New menu item like this:

    if (e.getActionCommand().equals("New"))
    newMethod();
    
    

Of course, for this to work, we also need to add a method named newMethod() to our class. For now, we're going to make it a very simple method that will just tell us which menu item was selected. One way to do it is with a message dialog, like this:

    private void newMethod()
{
    JOptionPane.showMessageDialog(frame, 
            "The  File > New  menu option was clicked", 
            "Menu Item Click", 
            JOptionPane.INFORMATION_MESSAGE);
}	
	

This newMethod() contains one call to JOptionPane's showMessageDialog() method, which is very similar to the showInputDialog() method we looked at in Lesson 5. It even uses the same parameters. The only difference is that this dialog method does not allow user input. It just displays information. And in this case, the information we want to display is about which menu option got clicked.

When we run the program with these two additions, then click File and New, here is what we see:





Menu click dialog

The message dialog pops up in front of the program window and stays there until we close it by clicking either the OK button or the red close button.

I'm going to let you fill in the code for the next three File menu items on your own. I'll show you my version of the program as soon as I explain how to make the last menu item, Exit, work.

The Exit menu item is different because we don't want it to perform an action and then come back to our program. We want it to close our window and end our program. And Java gives us a way to do that with a single call to a System method. Here it is:

    System.exit(0);
    
    

This command does just what we want. It needs one integer parameter, which provides a status code for any other program monitoring the system for such events. By convention, a status code of zero means the program completed successfully. Any other value means an abnormal termination. Since most error situations that would cause an abnormal termination can be handled with exceptions, you will rarely (if ever) see this call with a value other than zero.

To finish off this chapter, let's look at the current version of our program. It displays a pop-up dialog for each menu item except Exit, which shuts the window down and ends the program. To see it, click the link below.

Solution: Current version of program.

Chapter 4:
Inner Classes

We have one more topic for today. It relates to the GUI menu program we just wrote, and more specifically, to cleaning up the actionPerformed() method. You might wonder why it needs cleaning up. After all, it works, doesn't it?

Yes, it works, but it has some drawbacks. First, we removed the menu actions from the actionPerformed method to keep it from having to do a lot of different things, since that's not very good programming practice. But we still have a bunch of different branches in it that require a bunch of if . . . else if . . . logic. Wouldn't it be nice to be able to just get rid of all that?

Second, the if statements depend on menu item text. That's not very good practice, either. What if we wanted to change the text on a menu item? We would have to remember to change it in two places. Worse yet, what if we wanted to translate our application into another language? We'd have to change all the menu items in both places. It just gives us more chances to make mistakes.

Last, what we have is a menu listener starting a method that does nothing but call another method. That seems like a waste of a step. It makes more sense to have the listener call the second method directly, doesn't it?

We're going to settle all these issues with what Java calls inner classes. An inner class is a class that is defined inside another class called (naturally) the outer class. We can create a listener class inside our GUIMenu class. In fact, we can create any number of listener classes in there—one for each menu item, even—and each can manage its own action directly. That probably doesn't make much sense yet, so let me show you what I mean. Here's an example:

    public class GUIMenu
{
    // . . .
    menuItem = new JMenuItem("New");
    menuItem.addActionListener(new newListener());
    menu.add(menuItem);
    // . . .
    private class newListener implements ActionListener
    {
        public void actionPerformed(ActionEvent e)
        {
            JOptionPane.showMessageDialog(frame, 
                    "The  File > New  menu option was clicked", 
                    "Menu Item Click", 
                    JOptionPane.INFORMATION_MESSAGE);
        }
    }
    // . . .
}
	
    

There are several things I want you to notice about this example. First, our outer class, GUIMenu, is no longer an ActionListener and does not need to have implements ActionListener in its first line.

Next, when we add our listener to a menu, we don't add the outer class as the listener. Instead, we add a new object of a listener class specifically designed to listen to this menu item. Each menu item will have its own dedicated listener class to handle its action.

Also note that we don't have an actionPerformed() method in our outer GUIMenu class any longer. We don't need it since each menu item now has its own listener. That means we were also able to get rid of that long set of if statements that we needed to manage all the calls to the different menu action methods.

And last, you can see that we don't have a separate method defined for each menu item anymore. Instead, each menu item has its own inner class defined as an ActionListener, with its own actionPerformed() method, which defines the action to take for that specific menu item. While this can generate a lot of inner classes if a window has lots of menu items or other interactive elements, it's a more structured and object-oriented approach and is generally preferable to the first one we tried.

Give that technique a try. Go ahead and add separate listener classes for each menu item, then add an object of the correct listener type to each item. When you run the program, it shouldn't look or act any different than it did before. If you have any problems making it work, you can see my version here, but please try to set it up yourself before you look.

Before we move on, I'd like to pass along a little more information about inner classes:

  • Just as a matter of style, inner classes are usually placed at the end of the outer class, after all its methods.
  • Inner classes, since they are internal to another class, have access to all the data and methods in the outer class, even private elements. In that way, they're like methods. Notice, for example, that in our inner class above we used an outer class variable, frame, without any difficulty. Outer class methods can also be called from within an inner class.
  • Instances of inner classes are attached to instances of the outer class. The inner class instances do not exist independently and cannot be referenced independently of outer class instances.
  • Some programmers, when they only use an inner class one time, use a technique called anonymous inner classes. That technique goes one step further and defines inner classes without names right where they are used. In the example we wrote in this lesson, that would mean defining our five inner classes right in the argument list for the addActionListener() method. I prefer the method we used in this lesson because I think it's less complicated and easier to read. We won't be using anonymous inner classes in this course, but I wanted you to be familiar with the term in case you come across it.

If you would like more information on inner classes, there are a couple of links in the Supplementary Material section that will show you other ways to use them.

Menu Mnemonics and Accelerators

TQA-20 --- Go ahead, try saying "menu mnemonics" five times fast! Just kidding. Our last topic for today is how to set up keyboard shortcuts for menus and menu items. A mnemonic for a menu is a key that, in combination with the ALT key, will make the menu act just like you clicked the associated menu or item. In order for a mnemonic to work, the associated menu or item must be visible. An example of using mnemonics would be hitting ALT + F to pull down the File menu, then typing O to select the Open menu item.

There are also menu accelerators that will bypass the menu hierarchy and act like the menu item was clicked, whether or not the item was visible. For example, you could type CTRL + S to save, whether or not the Save menu item is visible.

Adding a mnemonic to a menu or a menu item is easy. Both types of objects have a method named setMnemonic() that takes one argument: an integer designating the key that will be the mnemonic. Calling the method not only sets the mnemonic, but it underlines the first occurrence of that letter in the menu as a visual cue. We can set the mnemonic for the File menu and the Open menu item like this:

    menu.setMnemonic(KeyEvent.VK_F);
menuItem.setMnemonic(KeyEvent.VK_O); 
	
    

The values for the argument are available in a class called KeyEvent, so we don't even have to worry about what key is what value. If you look at the KeyEvent class in the Java API, you'll see 100 or so key values, one for every possible key on the keyboard. For example, VK_A is the value for the A key, VK_Z is the value for the Z key, VK_TAB is the TAB key, VK_F12 is the F12 key, and so on.

As I said above, the setMnemonic() method automatically underlines the first instance of the letter you choose for the menu item. But what if you want to underline the second instance of the letter? For example, in the Save As . . . menu, the setMnemonic() method would normally underline the first A, the one in the word Save. But most applications underline the second A instead, the one in As. We can use the method setDisplayedMnemonicIndex() to change it, like this:

    menuItem.setDisplayedMnemonicIndex(5);
    
    

The first letter in the menu title is position zero, the second is position one, and if you keep counting you'll see that the A in As is position five. The call above underlines the letter in that position, so the menu shows up with the title Save As . . . , which is just the way we want it.

Unlike menu mnemonics, accelerator keys only work for menu items, not for menus. They are also set using a method call, this time setAccelerator(). Here is what this method call for the Save menu item looks like:

    menuItem.setAccelerator(
        KeyStroke.getKeyStroke(KeyEvent.VK_S,
                               Event.CTRL_MASK));
	
    

This call uses a method called getKeyStroke(), from the KeyStroke class, to simulate a keystroke involving two keys. This call tells Java that the accelerator key for the Save menu item is the letter S modified (or masked) by the CTRL key, which we normally write CTRL + S.

As our last exercise for this lesson, go ahead and set up the mnemonics ALT + N, ALT + O, ALT + S, ALT + A, and ALT + X for the New, Open, Save, Save As, and Exit menu items, respectively, in addition to the mnemonic ALT + F for the File menu. Also set up the accelerator keys CTRL + N, CTRL + O, CTRL + S, and CTRL + Q for the New, Open, Save, and Exit menu items. If you run the program after getting that set up and pull down the menu, it should look like this:



Menu with mnemonics and accelerators

If you have trouble, you can let me know in the Discussion Area. You can also compare your code to mine, which is here.

Chapter 5: Summary

Congratulations! You made it through another lesson. Once again, we've covered quite a bit of ground. Here's what we've seen today:

  • How to add a menu bar to a window using the JMenuBar class.

  • How to add menus to a menu bar with the JMenu class.

  • How to add menu items to a menu with the JMenuItem class.

  • How to design individual listeners for items we need to monitor for actions by using inner classes to organize them.

  • How to set up mnemonics and accelerators, which allow users to activate menu items from the keyboard as well as with a mouse.

I'd say that's enough for one lesson! What do you think?

I'll see you next time, when we'll continue to expand our knowledge of GUI tools with components like check boxes, radio buttons, text boxes, and images.


Lesson 7 FAQs

Q: I've seen applications with more than one bar at the top of the window. Why does Java limit us to one menu bar?

A: Applications with multiple bars at the top of the window still only have one menu bar. The rest are called toolbars and are built differently.


Q: These menus are very plain. Are there other menu options I can implement?

A: Yes, but I didn't have room to cover more than the basics in this lesson. Sun's menu tutorial includes more ideas, such as adding icons, check boxes, and radio buttons to menu items.

Lesson 7 Assignment

This lesson's assignment is to add at least two more menus to the window we have been working on. For example, you could add an Edit menu with Copy, Cut, Paste, Find, Replace or whatever other items you think are appropriate, and a Help menu with an About . . . item that displays information about your application. Or you can make up your own menus and items. Experiment with menus, items, inner class listeners, mnemonics, and accelerators until you are comfortable designing and using them.

Let me know what questions you come up with on the assignment. I'll see you in the Discussion Area.

Lesson 7 Quiz Answers

1. How many menus can you put on a menu bar?
There is no fixed limit.

2. How does Java determine the order of menus in the menu bar?
Menus are placed from left to right in the order that they are added to the menu bar.

3. What is an inner class?
A class that is defined inside another class.

4. How do you create submenus?
By adding a menu to a menu.

5. What is a menu mnemonic?
A keyboard shortcut that, when used with the ALT key, activates a visible menu or menu item as if it were clicked.


Lesson 7 Supplementary Materials